package tw.com.hitevision.whiteboard.android.render;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.Log;

import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import tw.com.hitevision.whiteboard.android.Props;
import tw.com.hitevision.whiteboard.android.R;
import tw.com.hitevision.whiteboard.android.display.DisplayDpiHelper;
import tw.com.hitevision.whiteboard.android.state.AutoshapeProperty;
import tw.com.hitevision.whiteboard.android.stateFile.AutoShapeStateFile;

/**
 * this class contains Autoshape's generate and control
 */

public class Autoshape extends BaseItem {
    private static final String TAG = Autoshape.class.getSimpleName();
    private static final float CIRCLE_CUBIC_CONSTANT = 0.551915f;
//    private static final float PADDING_SIZE = !Props.isFake4k()?10:20;
//    private static final int DETECT_OFFSET = !Props.isFake4k()?2:4;
//    private static final float DEFAULT_SIZE = !Props.isFake4k()?64:128;
//    private static final float MIN_SIZE = !Props.isFake4k()?6:12;

    private static float PADDING_SIZE;
    private static int DETECT_OFFSET;
    private static float DEFAULT_SIZE;
    private static float MIN_SIZE;
    @ColorInt
    private final int selectedOutlineColor;
    private PointF[] cornerPoint;
    private PointF startPoint;
    private PointF endPoint;
    private PointF thirdPoint;
    private PointF fourthPoint;
    private PointF tempPF;
    private PointF centerPoint;
    private RectF rectF;
    private float[] tempFloatArray;
    private float[] points;
    private float[] arrowHeadLeft;
    private float[] arrowHeadRight;


    private float width, height;
    private final Path detectPath;
    final Path path;
    private Context context;
    private Matrix movementMatrix;
    private AutoshapeProperty.Type type;
    private boolean filled;
    private float radius;
    private float degree;
    private int pointCounter;

    private final float originalThickness;
    private final float minThickness;
    private float thickness;
    private float realThickness;

    final RectF detectRectF;
    final Rect detectRect;
    final Region detectClipRegion;
    private Region detectRegion;

    public Autoshape(Context context, float x, float y, AutoshapeProperty.Type type, int color, boolean fill,float canvasRatio) {
        selectedOutlineColor = ContextCompat.getColor(context, R.color.selection_highlight);
        this.context = context;

        float factor = DisplayDpiHelper.getInstance().getFake4kFactor();
        PADDING_SIZE = context.getResources().getDimensionPixelSize(R.dimen.autoshape_padding_size) * factor;
        DETECT_OFFSET = (int) (context.getResources().getDimensionPixelOffset(R.dimen.autoshape_detect_offset) * factor);
        DEFAULT_SIZE = context.getResources().getDimensionPixelSize(R.dimen.autoshape_default_size) * factor;
        MIN_SIZE = context.getResources().getDimensionPixelSize(R.dimen.autoshape_min_size) * factor;

        path = new Path();
        detectPath = new Path();
        path.moveTo(x, y);
        tempPF = new PointF();
        movementMatrix = new Matrix();
        startPoint = new PointF(x, y);
        centerPoint = new PointF(x, y);
        thirdPoint = new PointF(x, y);
        fourthPoint = new PointF(x, y);
        endPoint = new PointF(x, y);
        rectF = new RectF();
        degree = 0;
        tempFloatArray = new float[2];
        points = new float[30];
        pointCounter = 0;
//        thickness = 4;
        minThickness = context.getResources().getDimensionPixelSize(R.dimen.autoshape_stroke_min_width) * factor;
        originalThickness = Math.max(context.getResources().getDimensionPixelSize(R.dimen.autoshape_thickness) * canvasRatio, minThickness) * factor;
        realThickness = originalThickness;
        thickness = originalThickness;
        int arrowOffset = (int) (context.getResources().getDimensionPixelOffset(R.dimen.autoshape_arrow_offset) * factor);
//        arrowHeadLeft = new float[] {10, -10};
//        arrowHeadRight = new float[] {-10, -10};
        arrowHeadLeft = new float[] {arrowOffset, -arrowOffset};
        arrowHeadRight = new float[] {-arrowOffset, -arrowOffset};

        arrowHeadLeft = new float[] {10, -10};
        arrowHeadRight = new float[] {-10, -10};
        detectRectF = new RectF();
        detectRect = new Rect();
        detectClipRegion = new Region();
        detectRegion = new Region();

        cornerPoint = new PointF[4];
        for (int i = 0; i < 4; i++) {
            cornerPoint[i] = new PointF();
        }
        width = 0;
        height = 0;
        this.type = type;
        setColor(color);
        this.filled = fill;
        setBounds(x, y, PADDING_SIZE, PADDING_SIZE);
    }

    private void updateStrokeWidth(float ratio) {
        float oldRealThickness = realThickness;
        realThickness = oldRealThickness * ratio;

        if (Double.isNaN(realThickness) || Double.isNaN(realThickness) || Double.isInfinite(realThickness) || Double.isInfinite(realThickness)) {
            realThickness = oldRealThickness;
        }

        thickness = Math.max(realThickness, minThickness);
    }

    @Override
    public void scaleFromPoint(float rateX, float rateY, PointF p) {
        movementMatrix.setScale(rateX, rateY, p.x, p.y);
        applyTransformToPoint(movementMatrix, startPoint);
        applyTransformToPoint(movementMatrix, endPoint);
        movementMatrix.mapPoints(points);
        if (rateX == rateY) {
            updateStrokeWidth(Math.abs(rateX));
        }
        generateNewPathFromPoint();
    }

    @Override
    public void rotateFromPoint(float degree, PointF p) {
        movementMatrix.setRotate(degree, p.x, p.y);
        applyTransformToPoint(movementMatrix, startPoint);
        applyTransformToPoint(movementMatrix, endPoint);
        movementMatrix.mapPoints(points);
        generateNewPathFromPoint();
    }

    @Override
    public void moveBy(float dx, float dy) {
        movementMatrix.setTranslate(dx, dy);
        applyTransformToPoint(movementMatrix, startPoint);
        applyTransformToPoint(movementMatrix, endPoint);
        movementMatrix.mapPoints(points);
        detectRectF.offset(dx, dy);
        generateNewPathFromPoint();
    }

    @Override
    public BaseItem copy() {
        Autoshape temp = new Autoshape(context, startPoint.x, startPoint.y, getType(), getColor(), filled, 1);
        temp.centerPoint.set(centerPoint);
        temp.thirdPoint.set(thirdPoint);
        temp.endPoint.set(endPoint);
        temp.fourthPoint.set(fourthPoint);
        temp.degree = degree;
        temp.pointCounter = pointCounter;
        temp.detectRectF.set(detectRectF);
        temp.detectRect.set(detectRect);
        temp.detectClipRegion.set(detectClipRegion);

        if (type == AutoshapeProperty.Type.ARROW) {
            temp.arrowHeadRight = arrowHeadRight.clone();
            temp.arrowHeadLeft = arrowHeadLeft.clone();
        }

        temp.cornerPoint = cornerPoint.clone();
        for (int i = 0; i < 4; i++) {
            temp.cornerPoint[i].set(cornerPoint[i]);
        }

        temp.points = points.clone();

        temp.width = getWidth();
        temp.height = getHeight();

        temp.generateNewPathFromPoint();
        return temp;
    }

    public static BaseItem copyFromStateFile(Context context, float x, float y, float width, float height,
                                             AutoshapeProperty.Type type, int color, boolean filled,
                                             PointF centerPoint, PointF thirdPoint, PointF endPoint, PointF fourthPoint, float degree,
                                             int pointCounter, RectF detectRectF, Rect detectRect, Region detectClipRegion,
                                             float[] arrowHeadLeft, float[] arrowHeadRight, PointF[] cornerPoint, float[] points) {
        Autoshape temp = new Autoshape(context, x, y, type, color, filled,1);
        temp.centerPoint.set(centerPoint);
        temp.thirdPoint.set(thirdPoint);
        temp.endPoint.set(endPoint);
        temp.fourthPoint.set(fourthPoint);
        temp.degree = degree;
        temp.pointCounter = pointCounter;
        temp.detectRectF.set(detectRectF);
        temp.detectRect.set(detectRect);
        temp.detectClipRegion.set(detectClipRegion);

        if (type == AutoshapeProperty.Type.ARROW) {
            temp.arrowHeadRight = arrowHeadRight.clone();
            temp.arrowHeadLeft = arrowHeadLeft.clone();
        }

        temp.cornerPoint = cornerPoint.clone();
        for (int i = 0; i < 4; i++) {
            temp.cornerPoint[i].set(cornerPoint[i]);
        }

        temp.points = points.clone();

        temp.width = width;
        temp.height = height;

        temp.generateNewPathFromPoint();
        return temp;
    }

    public AutoShapeStateFile getAutoShapeStateFile() {
        return new AutoShapeStateFile(this, filled, endPoint, thirdPoint, fourthPoint, centerPoint,
                degree, pointCounter, detectRectF, detectRect, detectClipRegion, arrowHeadLeft, arrowHeadRight, cornerPoint);
    }

    /**
     * this function is for setting a circle
     *
     * @param x center point's x
     * @param y center point's y
     * @param radius2 circle's width/height
     */
    public void circleSet(float x, float y, float radius2) {
        centerPoint.set(x, y);
        radius = radius2 / 2;
        width = radius2;
        height = radius2;
        cornerPoint[0].set(x - radius, y - radius);
        cornerPoint[1].set(x + radius, y - radius);
        cornerPoint[2].set(x + radius, y + radius);
        cornerPoint[3].set(x - radius, y + radius);

        addCirclePoints();
        generateNewPathFromPoint();
    }

    float getCenterX() {
        return centerPoint.x;
    }

    float getCenterY() {
        return centerPoint.y;
    }

    float getRadius() {
        return radius;
    }

    /**
     * this function is for setting a line
     *
     * @param start start point of line
     * @param end end point of line
     */

    public void linePointSet(PointF start, PointF end) {
        startPoint.set(start);
        endPoint.set(end);
        addLinePoints();
        generateNewPathFromPoint();
    }

    /**
     * this function is for setting a arrow
     *
     * @param start tail of arrow
     * @param end head of arrow
     */

    public void arrowSet(PointF start, PointF end) {
        float d;
        float diff;
        startPoint.set(start);
        endPoint.set(end);
        tempPF.set(endPoint);
        tempPF.offset(-startPoint.x, -startPoint.y);
        d = (float) Math.atan2(tempPF.x, tempPF.y);
        d = (float) Math.toDegrees(d);
        diff = degree - d;

        movementMatrix.setRotate(diff, 0, 0);
        movementMatrix.mapPoints(arrowHeadLeft);
        movementMatrix.mapPoints(arrowHeadRight);

        thirdPoint.set(end);
        thirdPoint.offset(arrowHeadLeft[0], arrowHeadLeft[1]);
        fourthPoint.set(end);
        fourthPoint.offset(arrowHeadRight[0], arrowHeadRight[1]);

        addArrowPoints();
        generateNewPathFromPoint();

        degree = d;

    }

    /**
     * this function is for setting oval
     *
     * @param x left of the rectangle fit the oval
     * @param y top of the rectangle fit the oval
     * @param width width of the rectangle fit the oval
     * @param height height of the rectangle fit the oval
     */

    public void ovalSet(float x, float y, float width, float height) {
        startPoint.set(x, y);
        endPoint.set(x + width, y + height);
        centerPoint.set(x + width / 2, y + height / 2);
        radius = width > height ? width / 2 : height / 2;
        cornerPoint[0].set(x, y);
        cornerPoint[1].set(x + width, y);
        cornerPoint[2].set(x + width, y + height);
        cornerPoint[3].set(x, y + height);
        this.width = width;
        this.height = height;

        rectF.set(x, y + height, x + width, y);
        path.rewind();
        addOvalPoints();
        generateNewPathFromPoint();

    }

    /**
     * this function is for setting square
     *
     * @param x left of square
     * @param y top of square
     * @param width width of square
     * @param height height of square
     */
    public void squareSet(float x, float y, float width, float height) {
        startPoint.set(x, y);
        endPoint.set(x + width, y + height);
        thirdPoint.set(x + width, y);
        fourthPoint.set(x, y + height);
        centerPoint.set(x + width / 2, y + height / 2);
        cornerPoint[0].set(x, y);
        cornerPoint[1].set(x + width, y);
        cornerPoint[2].set(x + width, y + height);
        cornerPoint[3].set(x, y + height);
        this.width = width;
        this.height = height;

        addSquarePoints();
        generateNewPathFromPoint();
    }

    /**
     * this function is for setting right triangle, S(sx ,sy) is start point, C(cx ,cy) is end point, E(ex ,ey) is third angle
     */
    public void rightTriangleSet(float sx, float sy, float cx, float cy, float ex, float ey) {
        startPoint.set(sx, sy);
        thirdPoint.set(ex, ey);
        endPoint.set(cx, cy);
        centerPoint.set((sx + cx + ex) / 3, (sy + cy + ey) / 3);

        this.width = Math.abs(sx - cx);
        this.height = Math.abs(sy - cy);

        addTrianglePoints();
        generateNewPathFromPoint();
    }

    /**
     * this function is for setting regular triangle, S(sx,sy) is start point which is a corner of largest square smaller than rectangle and fit the regular triangle, where D(dx, dy) is the diagonal corner of the rectangle.
     */
    public void regularTriangleSet(float sx, float sy, float dx, float dy) {
        float width = Math.abs(dx - sx);
        float height = Math.abs(dy - sy);
        float edge = width < height ? width : height;
        float leftX, rightX, botY;
        if (sy > dy) {
            // drag upward
            botY = sy;
            if (sx > dx) {
                // drag leftward
                leftX = sx - edge;
                rightX = sx;
            } else {
                // drag rightward
                leftX = sx;
                rightX = sx + edge;
            }
        } else {
            // drag downward
            botY = sy + edge;
            if (sx > dx) {
                // drag leftward
                leftX = sx - edge;
                rightX = sx;
            } else {
                // drag rightward
                leftX = sx;
                rightX = sx + edge;
            }
        }

        startPoint.set(leftX, botY);
        endPoint.set(rightX, botY);
        thirdPoint.set(leftX + edge / 2, botY - edge * (float) Math.sqrt(3) / 2);

        centerPoint.set((startPoint.x + fourthPoint.x + thirdPoint.x) / 3, (startPoint.y + fourthPoint.y + thirdPoint.y) / 3);

        this.height = edge;
        this.width = edge;
        addTrianglePoints();
        generateNewPathFromPoint();
    }

    /**
     * this function is for setting star. star will be consider as a regular circle to fit the square.
     *
     * @param x center point's x.
     * @param y center point's y.
     * @param radius width/height of the star
     * @param start rectangle's one corner
     * @param end rectangle's the other diagonal corner
     */
    public void starSet(float x, float y, float radius, PointF start, PointF end) {
        centerPoint.set(x, y);
        this.radius = radius / 2;
        startPoint.set(start);
        endPoint.set(end);

        this.width = radius * 2;
        this.height = radius * 2;

        addStarPoints();
        generateNewPathFromPoint();
    }

    /**
     * add points to generate the array of points to draw
     */
    private void addPoints(float x, float y) {
        points[pointCounter * 2] = x;
        points[pointCounter * 2 + 1] = y;
        pointCounter++;
    }

    private void addPoints(PointF p) {
        points[pointCounter * 2] = p.x;
        points[pointCounter * 2 + 1] = p.y;
        pointCounter++;
    }

    private void addCirclePoints() {
        float[] vertex = new float[] {centerPoint.x, centerPoint.y - radius};
        float[] supportPoint1 = new float[] {centerPoint.x + radius * CIRCLE_CUBIC_CONSTANT, centerPoint.y - radius};
        float[] supportPoint2 = new float[] {centerPoint.x + radius, centerPoint.y - radius * CIRCLE_CUBIC_CONSTANT};
        movementMatrix.setRotate(90, centerPoint.x, centerPoint.y);
        clearPoints();
        addPoints(vertex[0], vertex[1]);
        addPoints(supportPoint1[0], supportPoint1[1]);
        addPoints(supportPoint2[0], supportPoint2[1]);
        for (int i = 1; i < 4; i++) {
            movementMatrix.mapPoints(vertex);
            addPoints(vertex[0], vertex[1]);
            movementMatrix.mapPoints(supportPoint1);
            addPoints(supportPoint1[0], supportPoint1[1]);
            movementMatrix.mapPoints(supportPoint2);
            addPoints(supportPoint2[0], supportPoint2[1]);
        }
        movementMatrix.mapPoints(vertex);
        addPoints(vertex[0], vertex[1]);
    }

    private void addOvalPoints() {
        float rateX = (width / 2) / radius * 1.3f;
        float rateY = (height / 2) / radius * 1.3f;
        float[] vertex = new float[] {centerPoint.x, centerPoint.y - radius};
        float[] supportPoint1 = new float[] {centerPoint.x + radius * CIRCLE_CUBIC_CONSTANT, centerPoint.y - radius};
        float[] supportPoint2 = new float[] {centerPoint.x + radius, centerPoint.y - radius * CIRCLE_CUBIC_CONSTANT};

        movementMatrix.setRotate(90, centerPoint.x, centerPoint.y);

        clearPoints();
        addPoints(vertex[0], vertex[1]);
        addPoints(supportPoint1[0], supportPoint1[1]);
        addPoints(supportPoint2[0], supportPoint2[1]);
        for (int i = 1; i < 4; i++) {
            movementMatrix.mapPoints(vertex);
            addPoints(vertex[0], vertex[1]);
            movementMatrix.mapPoints(supportPoint1);
            addPoints(supportPoint1[0], supportPoint1[1]);
            movementMatrix.mapPoints(supportPoint2);
            addPoints(supportPoint2[0], supportPoint2[1]);
        }
        movementMatrix.mapPoints(vertex);
        addPoints(vertex[0], vertex[1]);
        movementMatrix.setScale(rateX, rateY, centerPoint.x, centerPoint.y);
        movementMatrix.mapPoints(points);
    }

    private void addLinePoints() {
        clearPoints();
        addPoints(startPoint);
        addPoints(endPoint);
    }

    private void addArrowPoints() {
        clearPoints();
        addPoints(startPoint);
        addPoints(endPoint);
        addPoints(thirdPoint);
        addPoints(endPoint);
        addPoints(startPoint);
        addPoints(endPoint);
        addPoints(fourthPoint);
    }

    private void addTrianglePoints() {
        clearPoints();
        addPoints(startPoint);
        addPoints(endPoint);
        addPoints(thirdPoint);
        addPoints(startPoint);
    }

    private void addSquarePoints() {
        clearPoints();
        addPoints(cornerPoint[0]);
        addPoints(cornerPoint[1]);
        addPoints(cornerPoint[2]);
        addPoints(cornerPoint[3]);
        addPoints(cornerPoint[0]);
    }

    private void addStarPoints() {
        float[] outside = new float[] {centerPoint.x, centerPoint.y - radius};
        float[] inside = new float[] {centerPoint.x, centerPoint.y - radius / 2};
        movementMatrix.setRotate(36, centerPoint.x, centerPoint.y);
        movementMatrix.mapPoints(inside);
        clearPoints();
        addPoints(outside[0], outside[1]);
        addPoints(inside[0], inside[1]);
        movementMatrix.setRotate(72, centerPoint.x, centerPoint.y);
        for (int i = 1; i < 5; i++) {
            movementMatrix.mapPoints(outside);
            movementMatrix.mapPoints(inside);
            addPoints(outside[0], outside[1]);
            addPoints(inside[0], inside[1]);
        }
        movementMatrix.mapPoints(outside);
        addPoints(outside[0], outside[1]);
    }

    private void clearPoints() {
        for (int i = 0; i < pointCounter * 2; i++) {
            points[i] = 0;
        }
        pointCounter = 0;
    }

    /**
     * build the path based on points
     */
    private void generateNewPathFromPoint() {
        path.rewind();
        path.moveTo(points[0], points[1]);
        setBounds(points[0], points[1], 0, 0);
        updateBounds(points[0], points[1]);
        if (type == AutoshapeProperty.Type.CIRCLE || type == AutoshapeProperty.Type.OVAL) {
            for (int i = 1; i < pointCounter; i += 3) {
                path.cubicTo(points[i * 2], points[i * 2 + 1], points[i * 2 + 2], points[i * 2 + 3], points[i * 2 + 4], points[i * 2 + 5]);
                updateBounds(points[i * 2], points[i * 2 + 1]);
                updateBounds(points[i * 2 + 2], points[i * 2 + 3]);
                updateBounds(points[i * 2 + 4], points[i * 2 + 5]);
            }
        } else {
            for (int i = 1; i < pointCounter; i++) {
                path.lineTo(points[i * 2], points[i * 2 + 1]);
                updateBounds(points[i * 2], points[i * 2 + 1]);
            }
        }

        generateDetectRegion();
    }

    private void updateBounds(float x, float y) {
        float left = getLeft();
        float right = getRight();
        float top = getTop();
        float bottom = getBottom();

        left = Math.min(x - thickness - PADDING_SIZE, left);
        right = Math.max(x + thickness + PADDING_SIZE, right);
        top = Math.min(y - thickness - PADDING_SIZE, top);
        bottom = Math.max(y + thickness + PADDING_SIZE, bottom);


        setBounds(left, top, right - left, bottom - top);
    }

    private void updateDetectPath() {
        float width = this.getWidth();
        float height = this.getHeight();

        detectPath.rewind();
        detectPath.set(path);

        if (width > height) {
            for (int i = pointCounter - 1; i >= 0; i--) {
                if (i % 2 == 0) {
                    detectPath.lineTo(points[i * 2] + DETECT_OFFSET, points[i * 2 + 1] + DETECT_OFFSET);
                    detectPath.lineTo(points[i * 2] - DETECT_OFFSET, points[i * 2 + 1] + DETECT_OFFSET);
                } else {
                    detectPath.lineTo(points[i * 2] - DETECT_OFFSET, points[i * 2 + 1] + DETECT_OFFSET);
                    detectPath.lineTo(points[i * 2] + DETECT_OFFSET, points[i * 2 + 1] + DETECT_OFFSET);
                }
            }
        } else {
            for (int i = pointCounter - 1; i >= 0; i--) {
                if (i % 2 == 0) {
                    detectPath.lineTo(points[i * 2] + DETECT_OFFSET, points[i * 2 + 1] + DETECT_OFFSET);
                    detectPath.lineTo(points[i * 2] + DETECT_OFFSET, points[i * 2 + 1] - DETECT_OFFSET);
                } else {
                    detectPath.lineTo(points[i * 2] + DETECT_OFFSET, points[i * 2 + 1] - DETECT_OFFSET);
                    detectPath.lineTo(points[i * 2] + DETECT_OFFSET, points[i * 2 + 1] + DETECT_OFFSET);
                }
            }
        }


    }

    public void rebuildArrow() {
        this.arrowSet(startPoint, endPoint);
    }

    private void applyTransformToPoint(Matrix matrix, PointF p) {
        tempFloatArray[0] = p.x;
        tempFloatArray[1] = p.y;
        matrix.mapPoints(tempFloatArray);
        p.set(tempFloatArray[0], tempFloatArray[1]);
    }

    public float[] getPoints() {
        return points;
    }

    private void generateDetectRegion() {
        if (filled || this.width < 5 * thickness || this.height < 5 * thickness) {

            if (type == AutoshapeProperty.Type.LINE) {
                if (detectRectF.height() < 20 && detectRectF.width() < 20) {
//
//                    path.computeBounds(detectRectF, true);
                    detectRectF.set(getLeft() + PADDING_SIZE - thickness, getTop() + PADDING_SIZE - thickness, getRight() - PADDING_SIZE + thickness, getBottom() - PADDING_SIZE + thickness);
                    detectRectF.roundOut(detectRect);

                    detectClipRegion.set(detectRect);
//                    detectRegion.setPath(path, detectClipRegion);
                    detectRegion.set(detectRect);

                } else {
                    updateDetectPath();
                    detectPath.computeBounds(detectRectF, true);
                    detectRectF.roundOut(detectRect);

                    detectClipRegion.set(detectRect);
                    detectRegion.setPath(detectPath, detectClipRegion);
                }
            } else {

//                updateDetectPath();
                path.computeBounds(detectRectF, true);

                if (detectRectF.height() < 20 || detectRectF.width() < 20) {
//
//                    path.computeBounds(detectRectF, true);
                    detectRectF.set(getLeft() + PADDING_SIZE - thickness, getTop() + PADDING_SIZE - thickness, getRight() - PADDING_SIZE + thickness, getBottom() - PADDING_SIZE + thickness);
                    detectRectF.roundOut(detectRect);

                    detectClipRegion.set(detectRect);
//                    detectRegion.setPath(path, detectClipRegion);
                    detectRegion.set(detectRect);

                } else {
                    detectRectF.roundOut(detectRect);

                    detectClipRegion.set(detectRect);
                    detectRegion.setPath(path, detectClipRegion);
                }

            }

        } else {
            updateDetectPath();
            detectPath.computeBounds(detectRectF, true);
            detectRectF.roundOut(detectRect);
            detectClipRegion.set(detectRect);
            detectRegion.setPath(detectPath, detectClipRegion);
        }
    }

    @Override
    public void getRegion(Region out) {
        out.setEmpty();
        if (detectRegion == null || detectRegion.isEmpty()) {
            generateDetectRegion();
            if (detectRegion == null) {
                log("too tiny to set region.");
            }
        }
        out.set(detectRegion);
    }

    @Override
    public void render(Canvas canvas, Paint paint, Context context) {
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStyle(Paint.Style.STROKE);

        //detect test
//        Paint p = new Paint();
//        p.setColor(Color.RED);
//        canvas.drawPath(detectPath,p);


        if (type != AutoshapeProperty.Type.ARROW && type != AutoshapeProperty.Type.LINE) {
            if (filled) {
                paint.setStyle(Paint.Style.FILL);
                paint.setColor(getColor());
//                paint.setStrokeWidth(thickness);
                canvas.drawPath(path, paint);

                paint.setStyle(Paint.Style.STROKE);
                if (isSelected() && !Props.getFlavorModel().equals(Props.Flavor.Model.Oem)) {
                    drawOutline(canvas, paint, 0);
                }
            } else {
                if (isSelected() && !Props.getFlavorModel().equals(Props.Flavor.Model.Oem)) {
                    drawOutline(canvas, paint, 5);
                }
                paint.setColor(getColor());
                paint.setStrokeWidth(thickness);
                canvas.drawPath(path, paint);
            }


        } else {
            if (isSelected() && !Props.getFlavorModel().equals(Props.Flavor.Model.Oem)) {
                drawOutline(canvas, paint, 5);
            }

            paint.setColor(getColor());
            paint.setStrokeWidth(thickness);

            canvas.drawPath(path, paint);
        }

    }

    public boolean isDistanceAcceptable() {
//        switch (type){
//            case CIRCLE:{
//                return radius>MIN_SIZE+thickness;
//            }
//            case REGULAR_TRIANGLE:{
//                return height>MIN_SIZE+thickness;
//            }
//            case STAR:{
//                return Math.abs(startPoint.x-endPoint.x)>MIN_SIZE+thickness;
//            }
//            default:{
//                break;
//            }
//        }
        if (type == AutoshapeProperty.Type.SQUARE || type == AutoshapeProperty.Type.LINE || type == AutoshapeProperty.Type.ARROW) {
            float x = Math.abs(startPoint.x - endPoint.x);
            float y = Math.abs(startPoint.y - endPoint.y);
            return x > MIN_SIZE || y > MIN_SIZE;
        }


        return width > MIN_SIZE || height > MIN_SIZE;
    }

    private void drawOutline(Canvas canvas, Paint paint, float increaseThickness) {
        paint.setStrokeWidth(thickness + increaseThickness);
        paint.setColor(selectedOutlineColor);
        canvas.drawPath(path, paint);
    }

    public AutoshapeProperty.Type getType() {
        return type;
    }

    public Path getPath() {
        return path;
    }

    public void defaultShapeSet(float x, float y) {
        PointF start = new PointF(), end = new PointF();
        start.set(x, y);
        end.set(x + DEFAULT_SIZE, y + DEFAULT_SIZE);
        switch (type) {
            case LINE: {
                linePointSet(start, end);
                break;
            }
            case OVAL: {
                ovalSet(x, y, DEFAULT_SIZE, DEFAULT_SIZE);
                break;
            }
            case STAR: {
                starSet(x, y, DEFAULT_SIZE / 2, start, end);
                break;
            }
            case ARROW: {
                arrowSet(start, end);
                break;
            }
            case CIRCLE: {
                circleSet(x, y, DEFAULT_SIZE);
                break;
            }
            case SQUARE: {
                squareSet(x, y, DEFAULT_SIZE, DEFAULT_SIZE);
                break;
            }
            case RIGHT_TRIANGLE: {
                rightTriangleSet(x, y, x + DEFAULT_SIZE, y + DEFAULT_SIZE, x, y + DEFAULT_SIZE);
                break;
            }
            case REGULAR_TRIANGLE: {
                regularTriangleSet(x, y, x + DEFAULT_SIZE, y + DEFAULT_SIZE);
                break;
            }
        }

    }

    private static void log(String msg) {
        Log.d(TAG, msg);
    }

}
